#include "factorymanager.h"
#include "connectionmanager.h"
#include "connectionconfig.h"
#include "factoryconfig.h"
#include "commandconfig.h"
#include <algorithm>
#include "utils.h"
#include <exception>
#include <stdexcept>
#include <activemq/core/ActiveMQConnectionFactory.h>

FactoryManager::FactoryManager(ConnectionConfig* config):config(config)
{
}

FactoryManager::~FactoryManager(void)
{
	Utils::deleteData(connections);
	Utils::deleteMap(factories);

}

void FactoryManager::send(const std::string& commandName,  google::protobuf::Message* message)
{

	ConnectionManager* connMgr = getOrCreateConnectionManager(commandName);
	connMgr->send(config->getCommandConfig(commandName), message);
}

void FactoryManager::listen(const std::string& commandName)
{
	CommandConfig* cmdCfg = config->getCommandConfig(commandName);
	if(cmdCfg == NULL)
	{
		throw std::logic_error(commandName + "'s config is not defined.");
	}
	ConnectionManager* connMgr = getOrCreateConnectionManager(commandName);
	connMgr->listen(cmdCfg);
}
void FactoryManager::stopListen(const std::string& commandName)
{
	ConnectionManager* connMgr = getConnectionManager(commandName);
	if(connMgr != NULL)
	{
		connMgr->stopListen(commandName);
	}
}

ConnectionManager* FactoryManager::getOrCreateConnectionManager(const std::string& cmdName)
{
	//get command config
	CommandConfig* cmdCfg = config->getCommandConfig(cmdName);
	if(cmdCfg == NULL)
	{
		throw std::logic_error(cmdName + "'s config is not defined.");
	}
	ConnectionManager* connMgr = getConnectionManager(cmdName);
	if(connMgr == NULL)
	{
		//get or create factory
		cms::ConnectionFactory* factory = getOrCreateFactory(cmdCfg->factoryName);
		//create connection manager
		connMgr = new ConnectionManager(cmdCfg->connectionName, factory);
		connMgr->open();
		connections.push_back(connMgr);
	}

	return connMgr;
}

ConnectionManager* FactoryManager::getConnectionManager(const std::string& cmdName)
{
	//get command config
	CommandConfig* cmdCfg = config->getCommandConfig(cmdName);
	if(cmdCfg == NULL)
	{
		throw std::logic_error(cmdName + "'s config is not defined.");
	}

	ConnectionManager* connMgr = Utils::findByName(connections, cmdCfg->connectionName);
	return connMgr;


}

cms::ConnectionFactory*  FactoryManager::getOrCreateFactory(const std::string& factoryName)
{
	cms::ConnectionFactory* factory = getFactory(factoryName);

	if(factory == NULL)
	{
		FactoryConfig* factoryCfg = config->getFactoryConfig(factoryName);
		if(factoryCfg == NULL)
		{
			throw std::logic_error(factoryName + "'s factory config is not defined.");
		}
		factory = new activemq::core::ActiveMQConnectionFactory(factoryCfg->url);
		factories.insert(std::pair<std::string, cms::ConnectionFactory*>( factoryName, factory));
	}

	return factory;
}

cms::ConnectionFactory* FactoryManager::getFactory(const std::string& factoryName)
{
	return Utils::findByKey(factories, factoryName);
}

void FactoryManager::removeFactory(std::string& factoryName)
{
	/*std::map<std::string, cms::ConnectionFactory*> factories;
	std::vector<ConnectionManager*> connections;*/
	for(std::map<std::string, cms::ConnectionFactory*>::const_iterator mit = factories.begin();
		mit != factories.end(); mit++)
	{
		if(mit->first == factoryName)
		{
			for(std::vector<ConnectionManager*>::const_iterator vit = connections.begin();
				vit != connections.end(); vit++)
			{
				if((*vit)->getConnectionFactory() == mit->second)
				{
					delete (*vit);
					vit = connections.erase(vit);
				}
			}
			delete mit->second;
			factories.erase(mit);
			return;
		}
	}

}
void FactoryManager::removeConnection(std::string& connectionName)
{
	for(std::vector<ConnectionManager*>::const_iterator it = connections.begin();
		it != connections.end(); it++)
	{
		if((*it)->getName() == connectionName)
		{
			delete (*it);
			connections.erase(it);
			return;
		}
	}
	return;
}

